//draw flow to png
//显示流程图
package main

import (
	"fmt"
	"io"
	"log"

	"github.com/BurntSushi/toml"
	"github.com/ajstarks/svgo"
)

func init() {
	//去掉下面的注释，显示 log 信息
	//var o NullWriter
	//log.SetOutput(&o)
}

const (
	space            = 50
	w                = 100
	h                = 35
	dpi      float64 = 72
	fontfile         = "./ukai.ttc"
	size     float64 = 20
)

type NodeT struct {
	Id     int
	Depend []int
	Status int // 0 can not start, 1 can do, 2 doing, 3 done
	Index  int //sort
}
type NodeMap map[string]*NodeT

type FlowConf struct {
	Title string
	Nodes NodeMap
}

func readConf(filename string) *FlowConf {
	ret := new(FlowConf)
	_, err := toml.DecodeFile(filename, ret)
	if err == nil {
		return ret
	} else {
		log.Printf("readConf: %v\n", err)
		return nil
	}
}

//OpNode 不同的 status 要显示不同的颜色
type OpNode struct {
	Name   string
	After  []int //IDs after this node, mean nodes depend this node
	Before []int // IDs before this node, mean depend nodes
	X, Y   int   //X、Y轴向的序号
	W, H   int   //节点方框的宽和高
	Px, Py int   //根据X、Y序号计算的像素点座标
	Status int   // 0 can not start, 1 can do, 2 doing, 3 done
	Depth  int
	Index  int //用于排序
	ID     int //ID
}

//MapOpNode map[ID]*OpNode
type MapOpNode map[int]*OpNode

func (self MapOpNode) Init() {
	depth := len(self)
	for id, v := range self {
		v.Depth = depth
		for _, v := range v.Before {
			self[v].After = append(self[v].After, id)
		}
	}
}

func (self MapOpNode) PrepareDraw() (sizeW, sizeH int) {
	var drawMap = make(map[int][]int)
	var N int = 0
	var finish bool
	var vecOpNode = make([]*OpNode, len(self))
	for _, v := range self {
		vecOpNode[v.Index] = v
	}
	for {
		finish = true
		for _, v := range vecOpNode {
			id := v.ID
			if v.Depth >= N {
				if len(v.Before) == 0 {
					v.Depth = N
					if v.Status == 0 {
						v.Status = 1 //can do
						log.Printf("Modify status:ID:%d 0->1\n", id)
					}
					finish = false
					drawMap[N] = append(drawMap[N], id)
				} else {
					bn := len(v.Before)
					statN := bn
					for _, bid := range v.Before {
						if self[bid].Depth < N {
							bn--
						}
						if self[bid].Status == 3 { //done
							statN--
						}
					}
					if bn == 0 {
						v.Depth = N
						finish = false
						drawMap[N] = append(drawMap[N], id)
					}
					if statN == 0 && v.Status == 0 {
						v.Status = 1 //can do
						log.Printf("Modify status:ID:%d 0->1\n", id)
					} else if statN > 0 && v.Status > 0 {
						log.Printf("Modify Status:ID:%d %d->0\n", id, v.Status)
						v.Status = 0
					}
				}
			}
		}
		if finish {
			break
		}
		N++
	}
	var maxWidth int
	var maxHeight = len(drawMap)
	for _, v := range drawMap {
		if len(v) > maxWidth {
			maxWidth = len(v)
		}
	}
	if (maxWidth % 2) == 0 {
		maxWidth++
	}
	//中轴序号
	var center = maxWidth / 2
	//计算图片尺寸
	var picW = (space+w)*maxWidth + space
	var picH = (space+h)*maxHeight + 2*space
	//平均分配空间
	for i := 0; i < N; i++ {
		v := drawMap[i]
		//sort.Ints(v)
		n := len(v)
		for idx := 0; idx < n; idx++ {
			id := v[idx]
			log.Printf("row:%d order:%d, id:%d\n", i, idx, id)
			node1 := self[id]
			node1.W = w
			node1.H = h
			node1.X = GetX(center, idx)
			node1.Y = i
			node1.Px = space + node1.X*(space+w)
			node1.Py = 2*space + i*(space+h)
		}
	}
	return picW, picH
}

func GetX(center, idx int) int {
	mod := idx % 2
	a := 1
	if mod == 1 {
		a = -1
	}
	return center + a*((idx/2)+mod)
}

func (self MapOpNode) DrawSVG(outFile io.Writer, w, h int) {
	canvas := svg.New(outFile)
	canvas.Start(w, h)
	canvas.Rect(0, 0, w, h, "fill:white")
	canvas.Text(space, space-10, Title, "id='title'", "text-anchor:left;font-size:30px;fill:black")
	for _, v := range self {
		var color = "black"
		switch v.Status {
		case 1:
			color = "green"
		case 2:
			color = "blue"
		case 0:
			color = "red"
		}
		canvas.Rect(v.Px, v.Py, v.W, v.H,
			fmt.Sprintf("fill:white;stroke:%s;stroke-width:1", color))
		//mx, my := v.Head()
		canvas.Text(v.Px+10, v.Py+25, v.Name, "name='ops'", fmt.Sprintf("id='%d'", v.ID),
			fmt.Sprintf("text-anchor:left;font-size:20px;fill:%s", color))
	}
	//draw link lines
	//定义箭头
	canvas.Def()
	canvas.Marker("arrow", 10, 5, 13, 8, "orient='auto'")
	canvas.Path("M2,2 L2,8 L10,5 L2,2", "fill: #000000")
	canvas.MarkerEnd()
	canvas.DefEnd()
	for _, v := range self {
		x0, y0 := v.Tail()
		for _, id := range v.After {
			if self[id].Depth-v.Depth == 1 {
				x1, y1 := self[id].Head()
				canvas.Line(x0, y0, x1, y1, "marker-end:url(#arrow);stroke:black;stroke-width:1;fill:none")
			} else if self[id].Depth-v.Depth > 1 {
				//三段式，中间线段走space空间
				x2, y2 := self[id].Head()
				a := 1
				if x2 < x0 {
					a = -1
				}
				dx1 := x0 + a*(v.W/2+space/2)
				dx2 := dx1
				dy1 := y0 + space
				dy2 := y2 - space
				canvas.Line(x0, y0, dx1, dy1, "stroke:black;stroke-width:1;fill:none")
				canvas.Line(dx1, dy1, dx2, dy2, "stroke:black;stroke-width:1;fill:none")
				canvas.Line(dx2, dy2, x2, y2, "marker-end:url(#arrow);stroke:black;stroke-width:1;fill:none")
			}
		}
	}
	canvas.End()
}

func (self *OpNode) Head() (x, y int) {
	return self.Px + self.W/2, self.Py
}
func (self *OpNode) Tail() (x, y int) {
	return self.Px + self.W/2, self.Py + self.H
}

func IsAlone(flowID, nodeID int) bool {
	map1, _, err := GetTreeByID(flowID)
	if err != nil {
		return false
	}
	map1.Init()
	node1 := map1[nodeID]
	if len(node1.Before) == 0 && len(node1.After) == 0 {
		return true
	} else {
		return false
	}
}

var Title string

func ShowSVG(flowID int, outFile io.Writer) {
	map1, title, err := GetTreeByID(flowID)
	Title = title
	if err != nil {
		panic(err)
	}
	map1.Init()
	for id, v := range map1 {
		log.Printf("Name:%s ID:%d Before:%+v After:%+v\n", v.Name, id, v.Before, v.After)
	}
	w, h := map1.PrepareDraw()
	map1.DrawSVG(outFile, w, h)
	log.Printf("W:%d H:%d\n", w, h)
	for id, v := range map1 {
		log.Printf("ID:%d X:%d Y:%d W:%d H:%d\n", id, v.X, v.Y, v.W, v.H)
	}
}
